package com.autumn.platform.dao.boot;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.mapping.DatabaseIdProvider;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;

import com.autumn.platform.core.boot.CoreAutoConfiguration;
import com.autumn.platform.dao.Configuration;
import com.autumn.platform.dao.HandlerRegister;
import com.autumn.platform.dao.binding.handler.IMapperHandler;
import com.autumn.platform.dao.binding.handler.impl.BatchMapperHandler;
import com.autumn.platform.dao.binding.handler.impl.FlushMapperHandler;
import com.autumn.platform.dao.binding.handler.impl.MergeMapperHandler;
import com.autumn.platform.dao.binding.handler.impl.OptionalMapperHandler;
import com.autumn.platform.dao.binding.handler.impl.SelectMapperHandler;
import com.autumn.platform.dao.binding.handler.impl.SpringDataPageMapperHandler;
import com.autumn.platform.dao.binding.handler.impl.UpdateMapperHandler;
import com.autumn.platform.dao.boot.DaoAutoConfiguration.DaoBeanAutoConfiguration;
import com.autumn.platform.dao.dialect.DialectDatabaseIdProvider;
import com.autumn.platform.dao.interceptor.StatementHandlerInterceptor;
import com.autumn.platform.dao.typehandler.BooleanTypeHandler;

@org.springframework.context.annotation.Configuration
//@ConditionalOnBean(DataSource.class)
@EnableConfigurationProperties(DaoProperties.class)
@AutoConfigureAfter({CoreAutoConfiguration.class, DataSourceAutoConfiguration.class})
@Import({DaoBeanAutoConfiguration.class, MybatisAutoConfiguration.class})
public class DaoAutoConfiguration implements ApplicationContextAware, InitializingBean, ConfigurationCustomizer {

    private ApplicationContext applicationContext;

    @SuppressWarnings("unused")
    private final DaoProperties properties;

    public DaoAutoConfiguration(DaoProperties properties) {
        super();
        this.properties = properties;
    }

    @Override
    public void customize(Configuration configuration) {
        configuration.getTypeHandlerRegistry().register(BooleanTypeHandler.class);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        List<IMapperHandler> mapperHandlers = this.getBeansOfType(IMapperHandler.class);
        HandlerRegister.getHandlerRegistry().setMapperHandlers(mapperHandlers);
    }

    @Bean
    @ConditionalOnClass(name = "java.util.Optional")
    @ConditionalOnMissingBean(name = "optionalMapperHandler")
    public OptionalMapperHandler optionalMapperHandler() {
        return new OptionalMapperHandler(-90);
    }

    @Bean
    @ConditionalOnMissingBean(name = "batchMapperHandler")
    public BatchMapperHandler batchMapperHandler() {
        return new BatchMapperHandler(-80);
    }
    
    @Bean
    @ConditionalOnMissingBean(name = "mergeMapperHandler")
    public MergeMapperHandler mergeMapperHandler() {
        return new MergeMapperHandler(-75);
    }

    @Bean
    @ConditionalOnMissingBean(name = "springDataPageMapperHandler")
    public SpringDataPageMapperHandler springDataPageMapperHandler() {
        return new SpringDataPageMapperHandler(-70);
    }

    @Bean
    @ConditionalOnMissingBean(name = "updateMapperHandler")
    public UpdateMapperHandler updateMapperHandler() {
        return new UpdateMapperHandler(70);
    }

    @Bean
    @ConditionalOnMissingBean(name = "selectMapperHandler")
    public SelectMapperHandler selectMapperHandler() {
        return new SelectMapperHandler(80);
    }

    @Bean
    @ConditionalOnMissingBean(name = "flushMapperHandler")
    public FlushMapperHandler flushMapperHandler() {
        return new FlushMapperHandler(90);
    }

    private <T> List<T> getBeansOfType(Class<T> cls) {
        Map<String, T> handlers = applicationContext.getBeansOfType(cls);
        List<T> values = new ArrayList<T>(handlers.values());
        AnnotationAwareOrderComparator.sort(values);
        return values;
    }

    @org.springframework.context.annotation.Configuration
    static class DaoBeanAutoConfiguration {
        @Bean
        @ConditionalOnMissingBean
        public DatabaseIdProvider databaseIdProvider() {
            return new DialectDatabaseIdProvider();
        }

        @Bean
        @ConditionalOnMissingBean(StatementHandlerInterceptor.class)
        public StatementHandlerInterceptor statementHandlerInterceptor() {
            return new StatementHandlerInterceptor();
        }
    }
}
